import math
import datetime
import pandas as pd
import backtrader as bt
from prettytable import PrettyTable
from sqlalchemy import create_engine
from pyLibs.Gadfly import StockStore, BackTraderStrategy as bts, BackTraderIndicator as bti, BackTraderUtits as btu
from Strategy import Bollinger
from pyLibs import GadflyUnits as units
from IPython.display import display
from backtrader_plotting import Bokeh
from backtrader_plotting.schemes import Tradimo


class MyStrategy(bts.BaseStrategy):
    def next(self):
        for i, data in enumerate(self.datas):
            if not self.trade['orderSign']:
                stock = self.getMultipleData(i)
                if stock.query("ema_s > middle & cycle == '60T'").shape[0] > 0:
                    inds = self.indicators[i]
                    if data.close[-1] < round(inds['ema_s'][-1], 3) and data.close[0] > round(inds['ema_s'][0], 3):
                        info = self.broker.getcommissioninfo(data)
                        symbol_multi = info.p.mult
                        total_value = self.broker.getvalue()
                        lots = math.floor(total_value / (symbol_multi * data.close[0]) / 100) * 100
                        platform_low = stock.loc[0, 'platform_low']
                        self.trade['platformLow'] = platform_low
                        self.trade['lossPrice'] = platform_low
                        self.buy(data, size=lots)
                        self.log('买入 信号, 交易单价: %.2f, 交易数量: %i, 当前仓位: %i, 初始止损价：%.2f' %
                                 (data.close[0], lots, self.getposition(data).size, self.trade['lossPrice']))
            else:
                inds= self.indicators[i]
                platform_low = inds['platform_low'][0]
                atr_loss = inds['atr_loss'][0]
                if platform_low > self.trade['platformLow']:
                    self.log('区间底部价格发生移动，现区间底部价格为: %.2f' % platform_low)
                    self.trade['platformLow'] = platform_low
                if atr_loss > self.trade['lossPrice']:
                    self.log('移动止损价格发生变化，现移动止损价格为: %.2f' % platform_low)
                    self.trade['lossPrice'] = atr_loss
                if data.close[-1] < self.trade['lossPrice'] and data.close[0] < self.trade['lossPrice']:
                    self.close(data, size=self.getposition(data).size, exectype=bt.Order.Limit, price=inds['stop_surplus'][0])
                    self.log('止盈 信号 (平仓), 限制单价: %.2f, 最高价: %.2f，最低价：%.2f' % (inds['stop_surplus'][0], data.high[0], data.low[0]))


def main():
    cerebro = bt.Cerebro()
    cerebro.broker.setcash(100000.0)
    cerebro.broker.set_slippage_fixed(fixed=0.001)  # 设置固定滑点
    cerebro.broker.setcommission(commission=0.0003)
    cerebro.broker.addcommissioninfo(btu.StockCommissionScheme())
    tdx = StockStore.TdxStore()
    stock = tdx.get_stock_data('600031', '15T')
    cerebro.addstrategy(MyStrategy)
    cerebro.adddata(btu.StockPandasData(dataname=stock.data,
                                        timeframe=stock.cycle.timeframe,
                                        compression=stock.cycle.compression),
                    name=stock.info.tdx_code)
    start_value = cerebro.broker.getvalue()
    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    cerebro.run()
    final_value = cerebro.broker.getvalue()
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    print('Net Profit: %.2f%%' % ((final_value - start_value) / start_value * 100))


class GadflyStrategy(bts.DoubleCycleStrategy):
    def next(self):
        for i, data in enumerate(self.datas):
            inds = self.indicators[i]
            cond = self.condition[i]
            if not self.trade['orderSign']:
                if cond['is_short_long_trend'][0]:
                    info = self.broker.getcommissioninfo(data)
                    symbol_multi = info.p.mult
                    total_value = self.broker.getvalue()
                    lots = math.floor(total_value / (symbol_multi * data.close[0]) / 100) * 100
                    message = '买入 信号, 交易单价: %.2f, 交易数量: %i, 初始止损价：%.2f，买入理由：%s'
                    if cond['ema_s_cross_up_mid'] or cond['close_cross_ema_s']:
                        if self.order:  # 取消之前未执行的订单
                            self.cancel(self.order)
                        reason = None
                        if cond['ema_s_cross_up_mid']:
                            reason = '趋势周期短期多头趋势，短期均线金叉中轨！'
                        if cond['close_cross_ema_s']:
                            reason = '趋势周期短期多头趋势，收盘价金叉短期均线，短期均线在中轨之上！'
                        self.trade['platformHigh'] = inds['platform_high'][0]
                        self.trade['platformLow'] = inds['platform_low'][0]
                        self.trade['lossPrice'] = inds['platform_low'][0]
                        self.order = self.buy(data, size=lots)
                        self.log(message % (data.close[0], lots, inds['platform_low'][0], reason))
            else:
                stop_surplus = data.close[0] if data.ratio > 0 else data.open[0]
                if data.close[0] > self.trade['platformHigh']:         # 收盘价突破前期区间的高点，上移止损价
                    self.trade['platformHigh'] = inds['platform_high'][0]
                    self.trade['lossPrice'] = inds['atr_loss'][0]
                    self.log('价格创出新高。移动止损价格发生变化，现止损价格为: %.2f' % self.trade['lossPrice'])
                if inds['platform_low'][0] > self.trade['platformLow']:
                    self.trade['platformLow'] = inds['platform_low'][0]
                    self.log('当前区间底部向上移动，现区间底部价格为: %.2f' % inds['platform_low'][0])
                if cond['morphology_rise_trend'][0]:
                    if self.order:  # 取消之前未执行的订单
                        self.cancel(self.order)
                    self.order = self.close(data, size=self.getposition(data).size)
                    self.log('出现K线卖出形态，发出卖出 信号 (平仓), 收盘价格: %.2f' % data.close[0])
                elif data.close[-1] > self.trade['lossPrice'] > data.close[0]:
                    if self.order:  # 取消之前未执行的订单
                        self.cancel(self.order)
                    self.order = self.close(data, size=self.getposition(data).size, exectype=bt.Order.Limit, price=stop_surplus)
                    self.log('跌破移动止损家，发出卖出 信号 (平仓), 限制单价: %.2f, 最高价: %.2f，最低价：%.2f' %
                             (inds['stop_surplus'][0], data.high[0], data.low[0]))
                elif data.close[-1] > self.trade['platformLow'] > data.close[0]:
                    if self.order:          # 取消之前未执行的订单
                        self.cancel(self.order)
                    self.order = self.close(data, size=self.getposition(data).size, exectype=bt.Order.Limit, price=stop_surplus)
                    self.log('跌破区间低点，发出卖出 信号 (平仓), 限制单价: %.2f, 最高价: %.2f，最低价：%.2f' %
                             (inds['stop_surplus'][0], data.high[0], data.low[0]))



def main1():
    config = units.loadYaml('./BackTrader/config.yaml')
    cerebro = bt.Cerebro()
    cerebro.broker.setcash(100000.0)
    cerebro.broker.set_slippage_fixed(fixed=0.001)  # 设置固定滑点
    cerebro.broker.setcommission(commission=0.0003)
    cerebro.broker.addcommissioninfo(btu.StockCommissionScheme())
    cerebro.addanalyzer(btu.WatchListAnalyzer, _name='trade_list')
    #cerebro.addstrategy(GadflyStrategy)
    cerebro.addstrategy(Bollinger.BollingerStrategy, config=config)
    tdx = StockStore.TdxStore(config)
    stock = tdx.doubleResample('000651', config.Feed)
    cerebro.adddata(btu.DoublePandasData(dataname=stock.data,
                                         timeframe=stock.cycle.timeframe,
                                         compression=stock.cycle.compression,
                                         fromdate=datetime.datetime(2021, 12, 1),
                                         todate=datetime.datetime(2023, 12, 31),
                                         ),
                    name=stock.info.tdx_code)
    start_value = cerebro.broker.getvalue()
    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    result = cerebro.run(tradehistory=True)

    trade_list = result[0].analyzers.trade_list.get_analysis()
    final_value = cerebro.broker.getvalue()
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    print('Net Profit: %.2f%%' % ((final_value - start_value) / start_value * 100))
    print('交易订单列表：')
    for key, value in trade_list.items():
        print(key)
        print(value)


def main2():
    config = units.loadYaml('./BackTrader/config.yaml')
    cerebro = bt.Cerebro()
    cerebro.broker.setcash(100000.0)
    cerebro.broker.set_slippage_fixed(fixed=0.001)  # 设置固定滑点
    cerebro.broker.setcommission(commission=0.0003)
    cerebro.broker.addcommissioninfo(btu.StockCommissionScheme())
    cerebro.addanalyzer(btu.WatchListAnalyzer, _name='trade_list')
    cerebro.addstrategy(Bollinger.SingleCycleBollingerStrategy, config=config)
    tdx = StockStore.TdxStore(config)
    stock = tdx.singleResample('000651', config.Feed)
    cerebro.adddata(btu.SinglePandasData(dataname=stock.data,
                                         timeframe=stock.cycle.timeframe,
                                         compression=stock.cycle.compression,
                                         fromdate=datetime.datetime(2021, 12, 1),
                                         todate=datetime.datetime(2023, 12, 31),
                                         ),
                    name=stock.info.tdx_code)
    start_value = cerebro.broker.getvalue()
    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    result = cerebro.run(tradehistory=True)
    trade_list = result[0].analyzers.trade_list.get_analysis()
    final_value = cerebro.broker.getvalue()
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    print('Net Profit: %.2f%%' % ((final_value - start_value) / start_value * 100))
    print('交易订单列表：')
    for key, pds in trade_list.items():
        print(key)
        #print(pds)
        display(pds.iloc[139:194])


class TestStrategy(bts.SingleCycleStrategy):
    def __init__(self):
        super().__init__()
        #self.config = config.Strategy

    def next(self):
        for i, data in enumerate(self.datas):
            inds = self.indicators[i]
            cond = self.condition[i]
            if str(data.datetime.date(0)) == datetime.datetime.now().strftime('%Y-%m-%d'):
                if cond['is_vol_release'][0] and data.volume[0] >= data.volume[-1] * 2 and cond['close_cross_ema_s'][0]:
                    pp = (data.datetime.datetime(0), data._name, data.close[0], inds['middle'][0], inds['upper'][0],
                          inds['lower'][0], inds['ema_s'][0], inds['ema_m'][0])
                    print('%s：%s，收盘价：%.3f，中轨：%.3f，上轨：%.3f，下轨：%.3f，EMA13：%.3f，EMA34：%.3f，满足条件！' % pp)
                else:
                    pp = (data.datetime.datetime(0), data._name, data.close[0], inds['middle'][0], inds['upper'][0],
                          inds['lower'][0], inds['ema_s'][0], inds['ema_m'][0], inds['bb'][0], inds['width'][0],
                          inds['atr_loss'][0], inds['platform_upper_high'][0], inds['platform_lower_low'][0])
                    print('%s：%s，收盘价：%.3f，中轨：%.3f，上轨：%.3f，下轨：%.3f，EMA13：%.3f，EMA34：%.3f，BB：%.3f，'
                          'Width：%.3f，ATR止损价：%.3f，平台上沿：%.3f，平台下沿：%.3f' % pp)
                    pass

def main3():
    config = units.loadYaml('./BackTrader/config.yaml')
    cerebro = bt.Cerebro()
    cerebro.addstrategy(TestStrategy)
    tdx = StockStore.TdxStore(config)
    stock5m = tdx.liveResampleData('000651', '5T', 100)
    feeds5m = btu.StockPandasData(dataname=stock5m.data,
                                  openinterest=-1,
                                  timeframe=bt.TimeFrame.Minutes,
                                  compression=5)
    stock15m = tdx.liveResampleData('000651', '15T', 100)
    feeds15m = btu.StockPandasData(dataname=stock15m.data,
                                   openinterest=-1,
                                   timeframe=bt.TimeFrame.Minutes,
                                   compression=15)
    stock60m = tdx.liveResampleData('000651', '60T', 100)
    feeds60m = btu.StockPandasData(dataname=stock60m.data,
                                   openinterest=-1,
                                   timeframe=bt.TimeFrame.Minutes,
                                   compression=60)
    cerebro.adddata(feeds5m, name=stock5m.info.tdx_code + '_5T')
    cerebro.adddata(feeds15m, name=stock15m.info.tdx_code + '_15T')
    cerebro.adddata(feeds60m, name=stock60m.info.tdx_code + '_60T')
    cerebro.run()
    #cerebro.plot(style='bar')
    #cerebro.plot(Bokeh(style='bar', plot_mode='single', scheme=Tradimo()))

if __name__ == '__main__':
    main3()